package mobileModule

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/jasonlvhit/gocron"
	"github.com/tarm/serial"

	"main/tools"
	"main/utils"
	"strconv"
	"strings"
	"time"
)

const (
	OperatorTypeCTCC     int = iota //0，电信
	OperatorTypeCMCCCUCC            //1，移动或联通
)

// 移动模块通信参数模板
type MobileModuleCommParamTemplate struct {
	Name       string `json:"name"`
	BaudRate   string `json:"baudRate"`   //波特率
	DataBits   string `json:"dataBits"`   //数据位: 5, 6, 7 or 8 (default 8)
	StopBits   string `json:"stopBits"`   //停止位: 1 or 2 (default 1)
	Parity     string `json:"parity"`     //校验: N - None, E - Even, O - Odd (default E),(The use of no parity requires 2 stop bits.)
	Timeout    string `json:"timeout"`    //通信超时（毫秒）
	Interval   string `json:"interval"`   //通信间隔（毫秒）
	PollPeriod string `json:"pollPeriod"` //轮询周期（秒）
}

// 移动模块运行参数模板
type MobileModuleRunParamTemplate struct {
	ICCID     string  `json:"iccid"` //SIM卡号
	IMEI      string  `json:"imei"`  //模块IMEI编号
	CSQ       string  `json:"csq"`   //信号强度
	Flow      string  `json:"flow"`  //手机流量
	CurFlow   float32 `json:"-"`
	Lac       string  `json:"lac"`                //基站定位数据
	Ci        string  `json:"ci"`                 //基站定位数据
	SIMInsert bool    `json:"simInsert"`          //手机卡是否插入
	Network   bool    `json:"netRegister"`        //网络注册状态
	Operator  int     `json:"operator,omitempty"` //运营商
}

// 移动模块配置参数模板
type MobileModuleConfigParamTemplate struct {
	FlowAlarmEnable bool   `json:"flowAlarm"`      //手机流量告警使能
	FlowAlarm       string `json:"flowAlarmValue"` //手机流量告警阈值
}

// 保活参数
type MobileModuleKeepAliveParamTemplate struct {
	IPMaster   string `json:"ipMaster"`   //保活检测IP1
	IPSlave    string `json:"ipSlave"`    //保活检测IP2
	PollPeriod int    `json:"pollPeriod"` //轮询周期（秒）
	OfflineCnt int    `json:"offlineCnt"` //离线判断次数
	CurrentCnt int    `json:"-"`
	ResetCnt   int    `json:"-"`
}

// 移动模块模板
type MobileModuleTemplate struct {
	Name           string                             `json:"name"`                //模块名称
	Model          string                             `json:"model"`               //模块型号
	CommParam      MobileModuleCommParamTemplate      `json:"commParam"`           //通信参数
	RunParam       MobileModuleRunParamTemplate       `json:"runParam"`            //模块参数
	ConfigParam    MobileModuleConfigParamTemplate    `json:"configParam"`         //配置参数
	KeepAliveParam MobileModuleKeepAliveParamTemplate `json:"keepAliveParam"`      //保活检测参数
	GPIOParam      MobileModuleGPIOParamTemplate      `json:"GPIOParam,omitempty"` //GPIO接口
	ReadBuffer     chan []byte                        `json:"-"`                   //通信接收缓存
	Port           *serial.Port                       `json:"-"`                   //通信句柄
	Status         bool                               `json:"status"`              //开关状态
	CancelFunc     context.CancelFunc                 `json:"-"`
	Scheduler      *gocron.Scheduler                  `json:"-"`
}

// 移动模块GPIO值参数模板
type MobileModuleGPIOValueParamTemplate struct {
	GPIO       string `json:"gpio"`
	SetValue   string `json:"setValue"`
	ResetValue string `json:"resetValue"`
}

// 移动模块GPIO参数模板
type MobileModuleGPIOParamTemplate struct {
	PowerCtr      MobileModuleGPIOValueParamTemplate `json:"powerCtr"`      //电源管脚
	ResetCtr      MobileModuleGPIOValueParamTemplate `json:"resetCtr"`      //复位管脚
	RSSILedHigh   MobileModuleGPIOValueParamTemplate `json:"RSSILedHigh"`   //信号强度高指示灯
	RSSILedMiddle MobileModuleGPIOValueParamTemplate `json:"RSSILedMiddle"` //信号强度中指示灯
	RSSILedLow    MobileModuleGPIOValueParamTemplate `json:"RSSILedLow"`    //信号强度低指示灯
}

// 移动模块参数模板
type MobileModuleParamTemplate struct {
	Name           string                             `json:"name"`                //模块名称
	Model          string                             `json:"model"`               //模块型号
	ConfigParam    MobileModuleConfigParamTemplate    `json:"configParam"`         //模块配置参数
	CommParam      MobileModuleCommParamTemplate      `json:"commParam"`           //模块通信参数
	KeepAliveParam MobileModuleKeepAliveParamTemplate `json:"keepAliveParam"`      //保活检测参数
	GPIOParam      MobileModuleGPIOParamTemplate      `json:"GPIOParam,omitempty"` //GPIO接口
	SleepTime      int                                `json:"sleepTime"`           //服务启动延时
}

var MobileModule *MobileModuleTemplate
var MobileModuleParam MobileModuleParamTemplate
var Past time.Time

// 初始化模块
func MobileModuleInit() {
	//获取移动模块配置参数

	if ReadMobileModuleParamFromJson() == false {
		MobileModule = &MobileModuleTemplate{}
		return
	}
	Past = time.Now()
	time.Sleep(time.Duration(MobileModuleParam.SleepTime) * time.Second)
	//实例化
	MobileModule = NewMobileModule(MobileModuleParam)
}

// 新的移动模块
func NewMobileModule(param MobileModuleParamTemplate) *MobileModuleTemplate {

	mobile := &MobileModuleTemplate{
		Name:           param.Name,
		Model:          param.Model,
		CommParam:      param.CommParam,
		ConfigParam:    param.ConfigParam,
		KeepAliveParam: param.KeepAliveParam,
		GPIOParam:      param.GPIOParam,
		ReadBuffer:     make(chan []byte, 1024),
		Status:         false,
	}

	ctx, cancel := context.WithCancel(context.Background())
	mobile.CancelFunc = cancel

	//打开串口
	mobile.MobileModuleOpen()

	//读取AT指令
	go mobile.MobileModuleReadATCommand(ctx)

	//上电后立马获取一次参数
	mobile.MobileModulePowerOn()

	//创建周期任务，定时获取参数，保活判断
	mobile.MobileModuleScheduler()

	return mobile
}

// 开启移动模块
func (m *MobileModuleTemplate) MobileModuleOpen() bool {
	serialParam := m.CommParam
	//配置波特率
	serialBaud, _ := strconv.Atoi(serialParam.BaudRate)
	//检验位
	var serialParity serial.Parity
	switch serialParam.Parity {
	case "N":
		serialParity = serial.ParityNone
	case "O":
		serialParity = serial.ParityOdd
	case "E":
		serialParity = serial.ParityEven
	}
	//停止位
	var serialStop serial.StopBits
	switch serialParam.StopBits {
	case "1":
		serialStop = serial.Stop1
	case "1.5":
		serialStop = serial.Stop1Half
	case "2":
		serialStop = serial.Stop2
	}

	serialConfig := &serial.Config{
		Name:        serialParam.Name,
		Baud:        serialBaud,
		Parity:      serialParity,
		StopBits:    serialStop,
		ReadTimeout: time.Millisecond * 1,
	}

	var err error
	m.Port, err = serial.OpenPort(serialConfig)
	if err != nil {
		fmt.Printf("移动模块串口接口[%s]打开失败 %v\n", m.CommParam.Name, err)
		return false
	}
	fmt.Printf("移动模块串口接口[%s]打开成功\n", m.CommParam.Name)
	m.Status = true

	return true
}

// 移动模块关闭
func (m *MobileModuleTemplate) MobileModuleClose() bool {
	if m.Port == nil {
		fmt.Printf("移动模块串口接口[%s] 文件句柄不存在\n", m.CommParam.Name)
		return false
	}
	err := m.Port.Close()
	if err != nil {
		fmt.Printf("移动模块串口接口[%s]关闭失败 %v\n", m.CommParam.Name, err)
		return false
	}
	fmt.Printf("移动模块串口接口[%s]关闭成功\n", m.CommParam.Name)
	m.Status = false
	return true
}

// 移动模块写数据
func (m *MobileModuleTemplate) MobileModuleWriteData(data string) int {

	if m.Port == nil {
		fmt.Printf("移动模块串口[%s]文件句柄不存在\n", m.CommParam.Name)
		m.MobileModuleOpen()
		return 0
	}

	cnt, err := m.Port.Write([]byte(data))
	if err != nil {
		fmt.Printf("移动模块写数据失败 %v\n", err)
	}

	return cnt
}

// 移动模块读取数据
func (m *MobileModuleTemplate) MobileModuleReadData(data []byte) int {

	if m.Port == nil {
		return 0
	}

	cnt, _ := m.Port.Read(data)

	return cnt
}

// MobileModuleScheduler
//
//	@Description:
//	@receiver m
//	@param ctx
//
// 移动模块调度程序

func (m *MobileModuleTemplate) MobileModuleScheduler() {
	//scheduler := gocron.NewScheduler()
	//定时获取参数
	m.Scheduler = gocron.NewScheduler()
	PollPeriod, _ := strconv.Atoi(m.CommParam.PollPeriod)
	_ = m.Scheduler.Every(uint64(PollPeriod)).Second().Do(m.MobileModuleGetParam)
	//定时判断保活
	_ = m.Scheduler.Every(uint64(m.KeepAliveParam.PollPeriod)).Second().Do(m.MobileModuleKeepAlive)

	m.Scheduler.Start()
	fmt.Printf("移动模块周期任务开始\n")

}

// 读AT指令
func (m *MobileModuleTemplate) MobileModuleReadATCommand(ctx context.Context) {
	//阻塞读
	rxBuf := make([]byte, 1024)
	rxBufCnt := 0
	fmt.Printf("移动模块数据接收任务开始\n")
	for {
		select {
		case <-ctx.Done():
			{
				fmt.Printf("移动模块数据接收任务结束\n")
				m.MobileModuleClose()
				return
			}
		default:
			{
				//阻塞读
				rxBufCnt = m.MobileModuleReadData(rxBuf)
				if rxBufCnt > 0 {
					//fmt.Printf("%s:curRxBufCnt %v", collName, rxBufCnt)
					//fmt.Printf("%s:CurRxBuf %X", collName, rxBuf[:rxBufCnt])

					//追加接收的数据到接收缓冲区
					m.ReadBuffer <- rxBuf[:rxBufCnt]
					//清除本次接收数据
					rxBufCnt = 0
				}
				time.Sleep(10 * time.Millisecond)
			}
		}
	}
}

// 写AT指令
func (m *MobileModuleTemplate) MobileModuleWriteATCommand(txCommand, rxCommand string, timeOut int) (error, string) {

	rxBuf := make([]byte, 0)
	rxBufLen := 0

	//判断串口是否打开
	if m.Status == false {
		m.MobileModuleOpen()
	}
	//发送AT指令
	m.MobileModuleWriteData(txCommand)
	timerOut := time.NewTimer(time.Duration(timeOut) * time.Millisecond)
	for {
		select {
		case <-timerOut.C: //等待超时
			{
				rxStr := string(rxBuf[:rxBufLen])
				fmt.Printf("移动模块接收超时,接收数据:[%s]\n", rxStr)
				return errors.New("移动模块接收超时\n"), ""
			}
		case buf := <-m.ReadBuffer:
			{
				bufLen := len(buf)
				if bufLen > 0 {
					rxBuf = append(rxBuf, buf[:bufLen]...)
					rxBufLen += bufLen
				}
				rxStr := string(rxBuf[:rxBufLen])
				if strings.Contains(rxStr, rxCommand) == false {
					continue
				}

				fmt.Printf("移动模块接收成功，接收数据:%s", rxStr)
				timerOut.Stop()
				return nil, rxStr
			}
		}
	}
}

// MobileModulePowerOn 上电后获取移动模块参数
func (m *MobileModuleTemplate) MobileModulePowerOn() {

	err, _ := m.MobileModuleWriteATCommand("AT\r\n", "OK", 200)
	//AT命令检测不通过，不进行下一步操作
	if err != nil {
		return
	}
	m.MobileModuleCheckSIM()
	m.MobileModuleGetICCID()
	m.MobileModuleGetIMEI()
	m.MobileModuleGetCSQ()
	//m.MobileModuleSetupDataCall()
	m.MobileModuleGetLocation(1)
}

// 移动模块获取参数、
func (m *MobileModuleTemplate) MobileModuleGetParam() {

	m.MobileModuleCheckSIM()
	m.MobileModuleGetICCID()
	m.MobileModuleGetIMEI()
	m.MobileModuleGetCSQ()
	//m.MobileModuleSetupDataCall()
	m.MobileModuleGetLocation(0)
	GetMobileModuleTime()
}

// 移动模块检查SIM卡
func (m *MobileModuleTemplate) MobileModuleCheckSIM() {
	if m.Model == "EC800N" {
		EC800NCheckSIM(m)
	}
}

// 移动模块获取ICCID
func (m *MobileModuleTemplate) MobileModuleGetICCID() {
	if m.Model == "EC800N" {
		EC800NGetICCID(m)
	}
}

// 移动模块获取IMEI
func (m *MobileModuleTemplate) MobileModuleGetIMEI() {
	if m.Model == "EC800N" {
		EC800NGetIMEI(m)
	}
}

// 移动模块读取信号强度
func (m *MobileModuleTemplate) MobileModuleGetCSQ() {
	if m.Model == "EC800N" {
		err := EC800NGetCSQ(m)
		if err != nil {
			fmt.Printf("移动模块信号强度接收失败\n")
		}
	}
}

// 移动模块读取网络状态
func (m *MobileModuleTemplate) MobileModuleGetLocation(cmd int) {
	if m.Model == "EC800N" {
		EC800NGetLocation(m, cmd)
	}
}

// MobileModuleGetOperator
//
//	@Description:
//	@receiver m
//
// 获取运营商信息
func (m *MobileModuleTemplate) MobileModuleGetOperator() {
	if m.Model == "EC800N" {
		EC800NGetOperator(m)
	}
}

// 拨号上网
func (m *MobileModuleTemplate) MobileModuleSetupDataCall() {
	if m.Model == "EC800N" {
		go EC800NSetupDataCall(m)
	}
}

// 读取流量
func (m *MobileModuleTemplate) MobileModuleFlowAdd(flow int) {

	m.RunParam.CurFlow += float32(flow)
	if m.RunParam.CurFlow <= 1024 {
		m.RunParam.Flow = fmt.Sprintf("%f Byte", m.RunParam.CurFlow)
	} else if m.RunParam.CurFlow > 1024 && m.RunParam.CurFlow <= 1048576 {
		m.RunParam.Flow = fmt.Sprintf("%4.2fK Byte", m.RunParam.CurFlow/1024)
	} else if m.RunParam.CurFlow > 1048576 && m.RunParam.CurFlow < 1073741824 {
		m.RunParam.Flow = fmt.Sprintf("%4.2fM Byte", m.RunParam.CurFlow/1048576)
	} else if m.RunParam.CurFlow > 1099511627776 {
		m.RunParam.Flow = fmt.Sprintf("%4.2fG Byte", m.RunParam.CurFlow/1099511627776)
	}
}

// 重启标志位
var MobileModuleResetFlag = 0

// 保活
func (m *MobileModuleTemplate) MobileModuleKeepAlive() {

	var err error
	if m.KeepAliveParam.IPMaster != "" {
		err, _ = tools.SendPing(m.KeepAliveParam.IPMaster)

	} else if m.KeepAliveParam.IPSlave != "" {
		err, _ = tools.SendPing(m.KeepAliveParam.IPSlave)
	}

	if err != nil {
		m.KeepAliveParam.CurrentCnt++
		fmt.Printf("CurrentCnt=%d,ResetCnt=%d\r\n", m.KeepAliveParam.CurrentCnt, m.KeepAliveParam.ResetCnt)
		if m.KeepAliveParam.CurrentCnt >= m.KeepAliveParam.OfflineCnt {
			m.KeepAliveParam.CurrentCnt = 0
			m.MobileModuleSetupDataCall()
			m.KeepAliveParam.ResetCnt++
			if m.KeepAliveParam.ResetCnt > 1 {
				m.KeepAliveParam.ResetCnt = 0
				MobileModuleResetFlag = 1
				m.Scheduler.Clear()

				m.MobileModuleClose()
				fmt.Printf("%v\r\n", m.GPIOParam.ResetCtr.GPIO)
				tools.GPIOWriteCmd(m.GPIOParam.ResetCtr.GPIO, m.GPIOParam.ResetCtr.SetValue)
				time.Sleep(time.Second * 2)
				tools.GPIOWriteCmd(m.GPIOParam.ResetCtr.GPIO, m.GPIOParam.ResetCtr.ResetValue)
				time.Sleep(time.Second * 10)

				m.MobileModuleScheduler()
				fmt.Println("重启完成")
				MobileModuleResetFlag = 0
				//m.MobileModuleOpen()
			}
		}
	} else {
		m.KeepAliveParam.CurrentCnt = 0
		m.KeepAliveParam.ResetCnt = 0
	}
}

// 从Json中读取移动模块参数
func ReadMobileModuleParamFromJson() bool {

	data, err := utils.FileRead("./config/mobileModule.json")
	if err != nil {
		//错误判断：文件是否存在
		if strings.Contains(err.Error(), "no such file or directory") {
			fmt.Printf("打开移动模块配置json文件失败,如果未创建文件，可以忽略\n")
		} else {
			fmt.Printf("打开移动模块配置json文件失败 %v\n", err)
		}
		return false
	}
	//将json文件中数据存到&MobileModuleParam
	err = json.Unmarshal(data, &MobileModuleParam)
	if err != nil {
		fmt.Printf("移动模块配置json文件格式化失败 %v\n", err)
		return false
	}
	fmt.Printf("打开移动模块配置json文件成功\n")

	return true
}

// 添加移动模块参数
func AddMobileModuleParam(param MobileModuleParamTemplate) error {

	if MobileModuleParam.Name == param.Name {
		return errors.New("移动模块名字已经存在\n")
	}

	//保存配置参数
	MobileModuleParam = param
	WriteMobileModuleParamToJson()

	//实例化
	MobileModule = NewMobileModule(MobileModuleParam)

	return nil
}

// 修改移动模块参数
func ModifyMobileModuleParam(param MobileModuleParamTemplate) error {

	if MobileModuleParam.Name != param.Name {
		return errors.New("移动模块名字不存在\n")
	}

	//保存配置参数
	MobileModuleParam = param
	WriteMobileModuleParamToJson()

	//重新实例化
	MobileModule.CancelFunc()
	MobileModule.Scheduler.Clear()

	//scheduler.Start()
	MobileModule = NewMobileModule(MobileModuleParam)

	return nil
}

// 删除移动模块参数
func DeleteMobileModuleParam(name string) error {

	if MobileModuleParam.Name != name {
		return errors.New("移动模块名字不存在\n")
	}

	//删除配置文件
	utils.DirIsExist("./config")
	err := utils.FileRemove("/config/mobileModule.json")
	if err != nil {
		fmt.Printf("删除移动模块配置json文件 %s %v\n", "失败", err)
		return err
	}

	//关闭协程
	MobileModule.CancelFunc()

	MobileModule = &MobileModuleTemplate{}

	return nil
}

// 将移动模块参数写入json
func WriteMobileModuleParamToJson() {

	utils.DirIsExist("./config")

	sJson, _ := json.Marshal(MobileModuleParam)
	err := utils.FileWrite("./config/mobileModule.json", sJson)
	if err != nil {
		fmt.Printf("写入移动模块配置json文件 %s %v\n", "失败", err)
		return
	}
	fmt.Printf("写入移动模块json文件 %s\n", "成功")
}

func GetMobileModuleTime() {

	// 获取当前时间
	now := time.Now()

	// 创建一个过去的时间点

	// 计算时间差
	duration := now.Sub(Past)

	// 格式化输出时间差（使用String方法）
	fmt.Println("时间差（使用String方法）:", duration)

	// 自定义格式化输出时间差（小时:分钟:秒）
	// 注意：这种方法需要手动计算小时、分钟和秒
	hours := int(duration.Hours())
	minutes := int(duration.Minutes()) % 60
	seconds := int(duration.Seconds()) % 60
	fmt.Printf("工作时间:%02d:%02d:%02d\n", hours, minutes, seconds)

	// 注意：上面的自定义格式化方法在处理小时和分钟时可能不够准确（特别是当分钟或秒接近60时），
	// 因为它直接对Minutes()和Seconds()的结果取模。更精确的方法是使用duration.Hours()来计算小时数，
	// 然后用剩余的纳秒数来计算分钟和秒数。但出于简化的目的，这里展示了基本的思路。

}
